/*
 *  Copyright 2010 Susanta Tewari.
 *  This file is part of genomemap.
 *
 *  genomemap is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  genomemap is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with genomemap.  If not, see <http://www.gnu.org/licenses/>.
 */
package genomemap.map;

import genomemap.data.GWData;
import genomemap.map.ContigMap.Contig;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.JPanel;

/**
 * It uses <code>MinimalContigMap</code>.
 *
 * @see MinimalContigMap
 *
 * @version 1.0 December, 2008
 * @author Susanta Tewari
 */
public class RepeatsDistMap extends JPanel {

    // <editor-fold defaultstate="collapsed" desc="Image Parameters">
    private int MAP_HEADER_VERT_INDENT = 40;

    private int MAP_HEADER_HORI_INDENT = 40;

    private int MAP_HEADER_SCALE_GAP = 10;

    private int MAP_SCALE_HORI_INDENT = 10;

    // keep it multiple of 2
    private int SCALE_TEXT_LINE_GAP = 12;

    // make it a multiple of 12 * 5 * int( each 200 kb gap)
    private int MAX_CH_PIX_SIZE = 600;

    private int INTER_CHROMOSOME_GAP = 50;

    private int CONTIG_HEIGHT = 15;

    private int MAP_LEGEND_HORI_GAP = -45;

    private int MAP_LEGEND_VERT_GAP = 30;

    private int LEGEND_WIDTH = 15;

    private int LEGEND_HEIGHT = 15;

    private int LEGEND_FIG_TEXT_GAP = 15;

    private int INTER_LEGEND_GAP = 20;

    private int LEGEND_HORI_PAD = 10;

    private int LEGEND_VERT_PAD = 10;

    private int MAX_CH_SIZE_MB = 12;

    private BasicStroke lineStroke = new BasicStroke(2.5f,
            BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

    private Font textFont = new Font("Courier New", Font.BOLD, 12);

    // input
    private String mapHeader = null;
    // </editor-fold>

    private int mapWIDTH = 0;

    private int mapHEIGHT = 0;

    private int probeSize = GWData.getInstance().getProbeLength();

    private List<ContigMap> contigMaps = new ArrayList<ContigMap>();

    public RepeatsDistMap() {
    }

    /**
     *
     * @param mapHeader
     * @param probeMaps
     */
    public RepeatsDistMap(String mapHeader, List<ProbeMap> probeMaps) {
        this.mapHeader = mapHeader;
        for (ProbeMap probeMap : probeMaps) contigMaps.add(new MinimalContigMap(probeMap));
        init();
    }

    /**
     * initialize the size of the graphics
     */
    private void init() {

        this.mapHEIGHT = 2 * MAP_HEADER_VERT_INDENT + this.getFontMetrics(
                textFont).getHeight() + MAP_HEADER_SCALE_GAP + contigMaps.size() * (INTER_CHROMOSOME_GAP
                + CONTIG_HEIGHT);
        this.mapWIDTH = 2 * MAP_SCALE_HORI_INDENT + SCALE_TEXT_LINE_GAP + MAX_CH_PIX_SIZE
                + MAP_LEGEND_HORI_GAP + LEGEND_FIG_TEXT_GAP + LEGEND_WIDTH + 2 * LEGEND_HORI_PAD + 2 * this.
                getFontMetrics(textFont).stringWidth("VII");

        setPreferredSize(new Dimension(mapWIDTH, mapHEIGHT));
        setBackground(Color.white);
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setStroke(lineStroke);

        // create background
        g2.setColor(Color.white);
        g2.fillRect(0, 0, mapWIDTH, mapHEIGHT);

        // set up text color and font and rendering hints
        g2.setColor(Color.black);
        g2.setFont(textFont);

        // map header
        g2.drawString(mapHeader, MAP_HEADER_HORI_INDENT,
                MAP_HEADER_VERT_INDENT);

        // scales : horizontal (12 MB divide in MB`s and each MB divided in
        // quarters)
        int horiScaleTextY = MAP_HEADER_VERT_INDENT + getFontMetrics(textFont).
                getHeight() + MAP_HEADER_SCALE_GAP;

        for (int mbIndex = 0; mbIndex <= MAX_CH_SIZE_MB; mbIndex++) {

            String label = "" + mbIndex;
            int x = (int) (MAP_SCALE_HORI_INDENT + SCALE_TEXT_LINE_GAP + ((double) MAX_CH_PIX_SIZE
                    / MAX_CH_SIZE_MB) * mbIndex);

            g2.drawString(label, x, horiScaleTextY);

            // bigger line - scale
            g2.drawLine(x, horiScaleTextY + SCALE_TEXT_LINE_GAP / 6, x,
                    horiScaleTextY + SCALE_TEXT_LINE_GAP);

            // smaller line - scale
            if (mbIndex < MAX_CH_SIZE_MB) {

                for (int subIndex = 1; subIndex < 5; subIndex++) {

                    int subX = x + (MAX_CH_PIX_SIZE / 60) * subIndex;
                    g2.drawLine(subX, horiScaleTextY + SCALE_TEXT_LINE_GAP / 2,
                            subX, horiScaleTextY + SCALE_TEXT_LINE_GAP);

                }
            }
        }

        g2.drawLine(MAP_SCALE_HORI_INDENT + SCALE_TEXT_LINE_GAP, horiScaleTextY
                + SCALE_TEXT_LINE_GAP, MAP_SCALE_HORI_INDENT + SCALE_TEXT_LINE_GAP + MAX_CH_PIX_SIZE,
                horiScaleTextY + SCALE_TEXT_LINE_GAP);

        // scale: vertical
        for (int i = 1; i <= contigMaps.size(); i++) {

            String label = "" + i;

            int y = horiScaleTextY + SCALE_TEXT_LINE_GAP + INTER_CHROMOSOME_GAP * i;

            int x = MAP_SCALE_HORI_INDENT - this.getFontMetrics(textFont).stringWidth(label);

            g2.drawString(label, x, y);

        }

        g2.drawLine(MAP_SCALE_HORI_INDENT + SCALE_TEXT_LINE_GAP,
                horiScaleTextY + SCALE_TEXT_LINE_GAP,
                MAP_SCALE_HORI_INDENT + SCALE_TEXT_LINE_GAP,
                horiScaleTextY + SCALE_TEXT_LINE_GAP + 8 * INTER_CHROMOSOME_GAP);

        // draw contigs
        int x = 0;
        int y = 0;
        int contigPixWidth = 0;

        for (int i = 0; i < contigMaps.size(); i++) {

            x = MAP_SCALE_HORI_INDENT + SCALE_TEXT_LINE_GAP ;

            // offset is used to align with chromosome numbers
            y = horiScaleTextY + SCALE_TEXT_LINE_GAP + INTER_CHROMOSOME_GAP * (i + 1) - this.
                    getFontMetrics(textFont).getHeight();

            final int CLONE_PIXEL_WIDTH = getMapPixelValue(probeSize);

            ContigMap contigMap = contigMaps.get(i);

            for (Iterator<Contig> itr = contigMap.iterator(); itr.hasNext();) {

                Contig contig = itr.next();
                contigPixWidth = getMapPixelValue(contig.getSize());

                // an enclosing rectangle for the contig
                g2.setColor(Color.black);
                g2.drawRect(x, y, contigPixWidth, CONTIG_HEIGHT);

                // draw probes and linking clones with colors
                for (String clone : contig.getClones()) {

                    g2.setColor(getCloneColor(clone));

                    g2.drawRect(x, y, CLONE_PIXEL_WIDTH, CONTIG_HEIGHT);

                    x += CLONE_PIXEL_WIDTH;

                }

                if (itr.hasNext())
                    x += getMapPixelValue(contigMap.contigGap(contig).intValue());

            }

            g2.setColor(Color.black);
            g2.drawString("" + contigMap.getContigCount() + "/" + contigMap.getProbes().size(),
                    x - 25, y-5);

        }

        // map legend
        int legend_box_x = MAP_SCALE_HORI_INDENT + SCALE_TEXT_LINE_GAP + MAX_CH_PIX_SIZE
                + MAP_LEGEND_HORI_GAP;

        int legend_box_y = MAP_HEADER_VERT_INDENT + this.getFontMetrics(textFont).
                getHeight() + MAP_HEADER_SCALE_GAP + SCALE_TEXT_LINE_GAP + MAP_LEGEND_VERT_GAP;

        int legend_box_width = 2 * LEGEND_HORI_PAD + LEGEND_FIG_TEXT_GAP + this.getFontMetrics(
                textFont).stringWidth("7") + LEGEND_WIDTH;

        int legend_box_height = 2 * LEGEND_VERT_PAD + contigMaps.size() * LEGEND_HEIGHT + (contigMaps.
                size() - 1) * INTER_LEGEND_GAP;

        g2.setColor(Color.black);
        g2.drawRect(legend_box_x, legend_box_y, legend_box_width,
                legend_box_height);

        int legend_y = legend_box_y + LEGEND_VERT_PAD;

        int fontHeight = this.getFontMetrics(textFont).getHeight();

        for (int i = 1; i <= contigMaps.size(); i++) {

            g2.setColor(Color.black);

            g2.drawString("" + i, legend_box_x + LEGEND_HORI_PAD,
                    legend_y + fontHeight);

            g2.setColor(getCloneColor(i));

            g2.fillRect(legend_box_x + LEGEND_HORI_PAD + LEGEND_FIG_TEXT_GAP,
                    legend_y, LEGEND_WIDTH, LEGEND_HEIGHT);

            g2.setColor(Color.black);

            g2.drawRect(legend_box_x + LEGEND_HORI_PAD + LEGEND_FIG_TEXT_GAP,
                    legend_y, LEGEND_WIDTH, LEGEND_HEIGHT);

            legend_y += LEGEND_HEIGHT + INTER_LEGEND_GAP;

        }

    }

    @Override
    public int getHeight() {
        return mapHEIGHT;
    }

    @Override
    public int getWidth() {
        return mapWIDTH;
    }

    private Color getCloneColor(String clone) {

        int linkGrpSize = GWData.getInstance().getRepeatCount(clone);

        return getCloneColor(linkGrpSize);
    }

    private Color getCloneColor(int repeat) {

        switch (repeat) {
            case 1:
                return Color.white;
            case 2:
                return Color.red;
            case 3:
                return Color.gray;
            case 4:
                return Color.yellow;
            case 5:
                return Color.green;
            case 6:
                return Color.blue;
            case 7:
                return Color.cyan;
            default:
                return Color.white;
        }
    }

    private int getMapPixelValue(double length) {

        return (int) (length / (MAX_CH_SIZE_MB * 1000) * MAX_CH_PIX_SIZE);
    }

}
